package org.zxp.onedoteight.stream;

import lombok.Builder;
import lombok.Data;
import org.junit.Test;

import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @program: effectiveJava
 * @description: ${description}
 * @author: X-Pacific zhang
 * @create: 2019-04-14 10:41
 **/
public class TestStream {
    //Stream并不是流，而是对集合类操作的一种方式，stream并不是某种数据结构，它只是数据源的一种视图
    //    无存储。stream不是一种数据结构，它只是某种数据源的一个视图，数据源可以是一个数组，Java容器或I/O channel等。
    //    为函数式编程而生。对stream的任何修改都不会修改背后的数据源，比如对stream执行过滤操作并不会删除被过滤的元素，而是会产生一个不包含被过滤元素的新stream。
    //    惰式执行。stream上的操作并不会立即执行，只有等到用户真正需要结果的时候才会执行。
    //    可消费性。stream只能被“消费”一次，一旦遍历过就会失效，就像容器的迭代器那样，想要再次遍历必须重新生成。
    //stream.forEach(s -> System.out.println(s));
    // stream.forEach(s -> System.out.println(s)); 报错：stream has already been operated upon or closed
    //解决中间加：.peek(k -> System.out.println(k))
    @Test
    public void testForeach() {
//        Stream<String> stream = Stream.of("a", "b", "c");
//        stream.forEach(s -> System.out.println(s));

        List<Order> list = Arrays.asList(new Order("1", 100, "定单1", "A"),
                new Order("2", 99, "定单2", "C"),
                new Order("3", 5000, "定单3", "B"),
                new Order("4", 378, "定单4", "C"),
                new Order("5", 22, "定单5", "A"));
        //给每个定单金额增加10000元
        list.stream().forEach(e -> e.setSumamount(e.getSumamount() + 10000));
        list.forEach(System.out::println);
    }


    @Test
    public void testCollectionStream() {
        //转化为list
        //List<String> list = Stream.of("a","b","c","d").collect(Collectors.toList());
        //list.forEach(System.out::println);

        //转化为map
        //Map map = Stream.of(
        //        Stu.builder().age(18).location("上海").build(),
        //        Stu.builder().age(17).location("深圳").build(),
        //        Stu.builder().age(20).location("北京").build(),
        //        Stu.builder().age(18).location("天津").build(),
        //        Stu.builder().age(18).location("广州").build()
        //).collect(Collectors.toMap(Stu::getLocation, t->t));
        //System.out.println(map);


        //转set
        //Set set = Stream.of("a","c","c","d").collect(Collectors.toSet());
        //System.out.println(set);

        //转treeset
        //TreeSet set = Stream.of("a","d","e","c").collect(Collectors.toCollection(TreeSet::new));
        //System.out.println(set);

        //转LinkedList
        LinkedList linkedList = Stream.of("a","d","e","c").collect(Collectors.toCollection(LinkedList::new));
        System.out.println(linkedList);
    }


    @Test
    public void testJoining(){
        //直接连成字符串
        List<String> list = Arrays.asList("A", "B", "C", "D");
        String result = list.stream().collect(Collectors.joining());
        System.out.println(result);
        //用-连起来
        System.out.println(list.stream().collect(Collectors.joining("-")));
        //用-连起来，最后在前后加[    ]
        System.out.println(list.stream().collect(Collectors.joining("-", "[", "]")));
    }

    @Test
    public void testPartitioningBy(){
        Stream.of("张新鹏","周琴","张桓语").collect(Collectors.partitioningBy(s -> s.length() > 2)).forEach((k,v) -> System.out.println("k="+k+" v="+v));
    }

    @Data
    @Builder
    private static class Stu{
        String name;
        int age;
        String location;
    }

    @Test
    public void testGroupingBy(){
        Map<String,List<Stu>> map = Stream.of(
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(17).location("上海").build(),
                Stu.builder().age(20).location("北京").build(),
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(18).location("广州").build()
        ).collect(Collectors.groupingBy(Stu::getLocation));
        map.forEach((k,v) -> {
            System.out.println("location_key:"+k);
            v.forEach(System.out::println);
        });
    }


    @Test
    public void testGroupingBy2(){
        Map<String,Long> map = Stream.of(
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(17).location("上海").build(),
                Stu.builder().age(20).location("北京").build(),
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(18).location("广州").build()
        ).collect(Collectors.groupingBy(Stu::getLocation,Collectors.counting()));
        map.forEach((k,v) -> {
            System.out.println("key:"+k + " value:"+v);
        });
    }

    @Test
    public void testSummarizing(){
        IntSummaryStatistics summarizing = Stream.of(
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(17).location("上海").build(),
                Stu.builder().age(20).location("北京").build(),
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(18).location("广州").build()
        ).collect(Collectors.summarizingInt(Stu::getAge));
        System.out.println(summarizing.getMax());//list中年龄的最大值
        System.out.println(summarizing.getMin());//list中年龄的最小值
        System.out.println(summarizing.getAverage());//list中年龄的平均值
        System.out.println(summarizing.getSum());////list中年龄的合
        System.out.println(summarizing.getCount());////list中的个数


        //Map<String, Map<String, LongSummaryStatistics>> map = 
        Map<String, LongSummaryStatistics> map = Stream.of(
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(17).location("上海").build(),
                Stu.builder().age(20).location("北京").build(),
                Stu.builder().age(18).location("上海").build(),
                Stu.builder().age(18).location("广州").build()
        ).collect(Collectors.groupingBy(Stu::getLocation, Collectors.summarizingLong(Stu::getAge)));//以lcoation分组后统计
        //).collect(Collectors.groupingBy(Stu::getLocation, Collectors.groupingBy(Stu::getName,Collectors.summarizingLong(Stu::getAge))));//以lcoation和name分组后统计
        map.forEach((k,v) -> {
            System.out.println("location_key:"+k+"==============");
            System.out.println(v.getMax());//对应分组中年龄的最大值
            System.out.println(v.getMin());//对应分组中年龄的最小值
            System.out.println(v.getAverage());//对应分组中年龄的平均值
            System.out.println(v.getSum());//对应分组中年龄的合
            System.out.println(v.getCount());//对应分组中的个数
        });
    }

    /**
     * 过滤长度大于3的
     */
    @Test
    public void testFilter() {
        Stream<String> stream = Stream.of("abc", "bc", "dfeg");
        stream.filter(s -> s.length() >= 3).forEach(s -> System.out.println(s));
    }

    @Test
    public void testDetinct() {
        Stream<String> stream = Stream.of("a", "a", "b", "c", "d");
        stream.distinct().forEach(s -> System.out.println(s));
    }

    /**
     * 倒序排列 sorted((s1,s2) -> ""大于0的表达式)
     */
    @Test
    public void testSort() {
        List<String> list = Arrays.asList("aaa", "wwwwww", "ccccccc", "eeee", "yyyyyyyy", "ooooo", "llllllllllllllll");
        List<String> result = null;
        // 默认升序进行排序
        result = list.stream().sorted().collect(Collectors.toList());
        //  按照字符串的长度进行排序（默认升序）Function是对输入的转化，在comparing中具体实现的compareTo，这里相当于用字符串的长度比较
        result = list.stream().sorted(Comparator.comparing(s -> s.length())).collect(Collectors.toList());
        //  按照字符串的长度进行排序（默认升序）
        //  comparing的参数是Function而String::length明显是个Supplier并且length方法不是个静态方法为什么这里却可以用呢?
        // 网上查阅了一些资料，应该是编译器做了一些手脚，
        // 在stackoverflow中是这样说的：传入一个String::length编译器已经明明确知道该怎么做了，按照一个特例记忆吧
        // https://stackoverflow.com/questions/43274306/comparator-comparing-throwing-non-static-reference-exception-while-taking-s
        result = list.stream().sorted(Comparator.comparing(String::length)).collect(Collectors.toList());
        // 降序写法1(配合String::length)
        result = list.stream().sorted(Comparator.comparing(String::length).reversed()).collect(Collectors.toList());
        // 降序写法2
        result = list.stream().sorted(Comparator.comparing(s -> s.length(), Comparator.reverseOrder())).collect(Collectors.toList());
        //取 top 3
        result = list.stream().sorted(Comparator.comparing(String::length)).limit(3).collect(Collectors.toList());

        System.out.println(result);
    }

    /**
     * The element type of the new stream
     * Function进行相关处理后返回一个对象，并让原始集合流生成一个新的集合流
     */
    @Test
    public void testMap() {
        List<Emp> list = Arrays.asList(new Emp("zxp1", "111", "1"),
                new Emp("zxp2", "222", "2"),
                new Emp("zxp3", "333", "3"),
                new Emp("zxp4", "444", "4"));
        List<EmpDto> result = list.stream().map(obj -> {
            return new EmpDto(obj.getName(), obj.getId());
        }).collect(Collectors.toList());
        result.forEach(s -> System.out.println(s));
    }

    @Test
    public void testPeek() {
        int a = 0;
//
//        stream.map(s -> s+1).forEach(s -> System.out.println(s));   这样可以，因为map中要求一个有返回值的表达式
//        stream.peek(s -> s+1).forEach(s -> System.out.println(s));   这样不可以 因为peek中要求一个无返回的表达式 “s+1;//这样不能作为一个无返回表达式”
        //peek
        Stream<Integer> stream = Stream.of(1, 2, 3, 4);
        stream.peek(s -> peek(s)).forEach(s -> System.out.println(s));
        System.out.println(index);
        //map
        Stream<Integer> stream2 = Stream.of(1, 2, 3, 4);
        stream2.map(s -> peek2(s)).forEach(s -> System.out.println(s));
        //map和peek都是中间做一些操作，但是不一样的是map会改变输出，但peek不会
        //forEach不能和peek一样，peek完事还能再续表达式
    }

    /**
     * 配合testPeek使用
     */
    int index = 0;

    public void peek(int s) {
        index += s;
    }

    /**
     * 配合testPeek使用
     */
    public int peek2(int s) {
        return s + 1;
    }

    @Test
    public void testMapFlat() {
        Stream<List<Integer>> stream = Stream.of(Arrays.asList(1, 4), Arrays.asList(3, 5, 6));
//        直接输出：
//        [1, 4]
//        [3, 5, 6]
        stream.flatMap(list -> list.stream()).forEach(s -> System.out.println(s));
    //    输出1 4 3 5 6
    }


    @Test
    public void testMerge() {
        List<StudentScore> list = buildATestList();
        Map<String, Double> studentScoreMap2 = new HashMap<>();
        list.forEach(studentScore -> studentScoreMap2.merge(
                studentScore.getStuName(),
                studentScore.getScore(),
                Double::sum));

        studentScoreMap2.forEach((k, v) -> {
            System.out.println(k + "    " + v);
        });
    }


    @Test
    public void testCompute() {
        List<StudentScore> list = buildATestList();
        Map<String, Double> studentScoreMap2 = new HashMap<>();
        list.forEach(studentScore -> studentScoreMap2.compute(
                studentScore.getStuName()
                , (k, v) -> {
                    System.out.println("k："+k);
                    System.out.println("v："+v);
                    if (v == null)
                        v = 1d;
                    else
                        v += 1;
                    return v;
                })
        );

        studentScoreMap2.forEach((k, v) -> {
            System.out.println(k + "    " + v);
        });
    }

    private List<StudentScore> buildATestList() {
        List<StudentScore> studentScoreList = new ArrayList<>();
        StudentScore studentScore1 = new StudentScore() {{
            setStuName("张三");
            setSubject("语文");
            setScore(70);
        }};
        StudentScore studentScore2 = new StudentScore() {{
            setStuName("张三");
            setSubject("数学");
            setScore(80);
        }};
        StudentScore studentScore3 = new StudentScore() {{
            setStuName("张三");
            setSubject("英语");
            setScore(65);
        }};
        StudentScore studentScore4 = new StudentScore() {{
            setStuName("李四");
            setSubject("语文");
            setScore(68);
        }};
        StudentScore studentScore5 = new StudentScore() {{
            setStuName("李四");
            setSubject("数学");
            setScore(70);
        }};
        StudentScore studentScore6 = new StudentScore() {{
            setStuName("李四");
            setSubject("英语");
            setScore(90);
        }};
        StudentScore studentScore7 = new StudentScore() {{
            setStuName("王五");
            setSubject("语文");
            setScore(80);
        }};
        StudentScore studentScore8 = new StudentScore() {{
            setStuName("王五");
            setSubject("数学");
            setScore(85);
        }};
        StudentScore studentScore9 = new StudentScore() {{
            setStuName("王五");
            setSubject("英语");
            setScore(70);
        }};

        studentScoreList.add(studentScore1);
        studentScoreList.add(studentScore2);
        studentScoreList.add(studentScore3);
        studentScoreList.add(studentScore4);
        studentScoreList.add(studentScore5);
        studentScoreList.add(studentScore6);
        studentScoreList.add(studentScore7);
        studentScoreList.add(studentScore8);
        studentScoreList.add(studentScore9);

        return studentScoreList;
    }


    public static class StudentScore {
        String stuName;
        String subject;
        double score;

        public String getStuName() {
            return stuName;
        }

        public void setStuName(String stuName) {
            this.stuName = stuName;
        }

        public String getSubject() {
            return subject;
        }

        public void setSubject(String subject) {
            this.subject = subject;
        }

        public double getScore() {
            return score;
        }

        public void setScore(double score) {
            this.score = score;
        }
    }


    @Test
    public void testMax() {
        List<Order> list = Arrays.asList(new Order("1", 100, "定单1", "A"),
                new Order("2", 99, "定单2", "C"),
                new Order("3", 5000, "定单3", "B"),
                new Order("4", 378, "定单4", "C"),
                new Order("5", 22, "定单5", "A"));
        Optional<Order> optionalOrder = null;
        optionalOrder = list.stream().max((x,y) -> Integer.compare(x.getSumamount(), y.getSumamount()));
        // 用getSumamount作为排序（默认升序）的依据，取max
        optionalOrder = list.stream().max(Comparator.comparingInt(s -> s.getSumamount()));
        // 参见sort部分的说明
        optionalOrder = list.stream().max(Comparator.comparingInt(Order::getSumamount));
        optionalOrder = list.stream().min(Comparator.comparingInt(Order::getSumamount));
        System.out.println(optionalOrder.get());
    }

    @Test
    public void testskip() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        numbers.stream().skip(2).forEach(s -> System.out.println(s));
        //3, 4, 5, 6, 7, 8, 9, 10
    }


    @Test
    public void testiterate() {
        Stream.iterate(0, n -> n + 1).limit(10).forEach(i -> System.out.println(i));

        List<String> list = Arrays.asList("A", "B", "C", "D", "E");
        Stream.iterate(0, n -> n + 1).limit(list.size()).forEach(i -> System.out.println(list.get(i)));


//        Stream.iterate(0, n -> n + 1).limit(3).
//                parallel().map(
//                a -> persons.stream().skip(a * 5).limit(5).parallel().collect(Collectors.toList())
//        ).collect(Collectors.toList());
//
//
//        Stream.iterate(0, n -> n + 1).limit(limit).parallel()
//                .map(
//                        a -> oriList.stream().skip(a * Constant.BULK_COUNT).limit(Constant.BULK_COUNT).parallel().collect(Collectors.toList())
//                ).collect(Collectors.toList());
    }


    @Test
    public void testAllMatch() {
        List<Order> list = Arrays.asList(new Order("1", 100, "定单1", "A"),
                new Order("2", 99, "定单2", "C"),
                new Order("3", 5000, "定单3", "B"),
                new Order("4", 378, "定单4", "C"),
                new Order("5", 22, "定单5", "A"));
        System.out.println(list.stream().allMatch(s -> s.getSumamount() > 10));//true
        System.out.println(list.stream().allMatch(s -> s.getSumamount() > 100));//false
        System.out.println(list.stream().anyMatch(s -> s.getSumamount() > 100));//true
    }


    @Test
    public void testReduce(){
        int value = Stream.of(1, 2, 3, 4, 5)
                .reduce((item1, item2) -> item1 + item2).get();
        System.out.println(value);
        //1+2+3+4+5=15
    }


}
